package mvc.core;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import mvc.core.annotation.Action;
import mvc.core.annotation.Batis;
import mvc.core.annotation.DELETE;
import mvc.core.annotation.GET;
import mvc.core.annotation.INPUT;
import mvc.core.annotation.Impl;
import mvc.core.annotation.Inject;
import mvc.core.annotation.Mapping;
import mvc.core.annotation.POST;
import mvc.core.annotation.PUT;
import mvc.core.annotation.Service;
import mvc.core.config.ConfigContext;
import mvc.core.log.Logger;
import mvc.core.util.Util;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * Bean工厂
 * 
 * @author liuzy
 * @version 2015年5月13日
 */
public class BeanFactory {
	
	private static Logger logger = Logger.getLogger(BeanFactory.class);
	
	protected static Set<Class<?>> allClasses = null;
	
    /** Bean仓库 */
    private static Map<Class<?>, Object> beanMap = new HashMap<Class<?>, Object>();
    
    /** Mapping仓库 */
    private static Map<String, ActionMapping> actionMappingMap = new HashMap<String, ActionMapping>();
    
    /**
     * 读xml加载并注入Bean
     * @param fileName
     */
    protected static void loadBeanByXml(String fileName) {
    	logger.debug("读"+fileName+"开始...");
		try {
			// 读取指定的配置文件
			SAXReader reader = new SAXReader();
			ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
			// 从class目录下获取指定的xml文件
			InputStream ins = classLoader.getResourceAsStream(fileName);
			Document doc = reader.read(ins);
			Element _beans = doc.getRootElement();
			Element _bean;
			// 遍历bean
			for (Iterator<?> _beans_i = _beans.elementIterator("bean"); _beans_i.hasNext();) {
				_bean = (Element) _beans_i.next();
				// 获取bean的属性id和class
//				String id = _bean.attribute("id").getText();
				String clazzName = _bean.attribute("class").getText();
				Class<?> beanClass = Class.forName(clazzName);
				Field[] beanFields = beanClass.getDeclaredFields();
				Object obj = beanClass.newInstance();
				// 遍历该bean的property属性
				for (Iterator<?> _bean_i = _bean.elementIterator("property"); _bean_i.hasNext();) {
					Element _property = (Element) _bean_i.next();
					// 获取该property的name和value
					String name = _property.attribute("name").getText().trim();
					String value = _property.attribute("value").getText().trim();
					if (value == null) {
						value = ((Element) _property.elementIterator("value").next()).getText().trim();
					}
					if (value != null && value.startsWith("${") && value.endsWith("}")) {
						value = ConfigContext.get(value.substring(2, value.length()-1));
					}
					// 把属性值set到对象
					for (Field field : beanFields) {
						if (name.equals(field.getName())) {
							field.setAccessible(true);// 必须使该字段可访问
							field.set(obj, value);
							break;
						}
					}
				}
				logger.debug("载入Bean:" + clazzName);
				beanMap.put(beanClass, obj);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
    	logger.debug("读"+fileName+"结束");
	}
    
    /**
     * 扫描注解加载Bean
     */
    protected static void loadBeanByAnnotations() {
    	logger.debug("扫描注解加载Bean开始...");
        try {
        	allClasses = ClassScanner.scanner();
        	for (Class<?> cls : allClasses) {
    			if (cls.isAnnotationPresent(Action.class)) {
    				logger.debug("载入ActionBean:" + cls.getName());
    				beanMap.put(cls, (Object) cls.newInstance());
    				String actionPath = "";
    		    	if (cls.isAnnotationPresent(Mapping.class)) {
    		    		actionPath += "/" + cls.getAnnotation(Mapping.class).value();
    				}
    		    	Method[] methods = cls.getMethods();
    				for (Method method : methods) {
    					if (method.isAnnotationPresent(Mapping.class)) {
    						String path = Util.reSlash(actionPath + "/" + method.getAnnotation(Mapping.class).value());
    						List<String> types = new ArrayList<>();
    						if (method.isAnnotationPresent(GET.class)) types.add("GET");
    						else if (method.isAnnotationPresent(POST.class)) types.add("POST");
    						else if (method.isAnnotationPresent(PUT.class)) types.add("PUT");
    						else if (method.isAnnotationPresent(DELETE.class)) types.add("DELETE");
    						else if (method.isAnnotationPresent(INPUT.class)) types.add("INPUT");
    						else types.addAll(Arrays.asList("GET","POST","PUT","DELETE","INPUT"));
							actionMappingMap.put(path, new ActionMapping(path, types, cls, method));
    						logger.debug("添加映射:path="+path+", type="+types+" action="+cls.getName()+", method="+method.getName());
    					}
    				}
    			} else if (cls.isAnnotationPresent(Service.class)) {
    				logger.debug("载入ServiceBean:" + cls.getName());
    				beanMap.put(cls, (Object) cls.newInstance());
    			} else if (cls.isAnnotationPresent(Batis.class)) {
    				logger.debug("载入BatisBean:" + cls.getName());
    				beanMap.put(cls, (Object) new MapperProxyFactory<>(cls).newInstance());
    			}
    		}
        } catch (InstantiationException e1) {
            e1.printStackTrace();
        } catch (IllegalAccessException e2) {
            e2.printStackTrace();
        }
        logger.debug("扫描注解加载结束");
    }
	
    /**
     * 扫描注解注入Bean属性
     */
    protected static void iocBean() {
        logger.debug("注入Bean属性开始...");
		try {
			for (Map.Entry<Class<?>, Object> beanEntry : beanMap.entrySet()) {
				// 获取 Bean 类与 Bean 实例
				Class<?> beanClass = beanEntry.getKey();
				Object beanInstance = beanEntry.getValue();
				// 获取 Bean 类中所有的字段（不包括父类中的方法）
				Field[] beanFields = beanClass.getDeclaredFields();
				if (beanFields != null) {
					// 遍历所有的 Bean 字段
					for (Field beanField : beanFields) {
						// 判断当前 Bean 字段是否带有 @Inject 注解
						if (beanField.isAnnotationPresent(Inject.class)) {
							// 获取 Bean 字段对应的接口
							Class<?> interfaceClass = beanField.getType();
							// 判断接口上是否标注了 @Impl 注解
							Class<?> implClass = null;
							if (interfaceClass.isAnnotationPresent(Impl.class)) {
								// 获取强制指定的实现类
								implClass = interfaceClass.getAnnotation(Impl.class).value();
							} else if(interfaceClass.isAnnotationPresent(Service.class)) {
								implClass = interfaceClass;
							} else if(interfaceClass.isAnnotationPresent(Batis.class)) {
								implClass = interfaceClass;
							} else {
								// 寻找接口所有的实现类
								List<Class<?>> implClassList = new ArrayList<>();
								for (Class<?> cls : allClasses) {
					    			if (Util.isImpl(cls, interfaceClass))
					    				implClassList.add(cls);
					    			}
								if (implClassList.size() > 0) {
									// 获取第一个实现类
									implClass = implClassList.get(0);
								} else
									logger.error("注入"+beanClass.getName()+"的属性"+beanField.getName()+"失败,未找到实现类");
							}
							// 若存在实现类，则执行以下代码
							if (implClass != null) {
								// 从 Bean Map 中获取该实现类对应的实现类实例
								Object implInstance = beanMap.get(implClass);
								// 设置该 Bean 字段的值
								if (implInstance != null) {
									beanField.setAccessible(true); // 取消类型安全检测（可提高反射性能）
									beanField.set(beanInstance, implInstance);
									logger.debug("注入"+beanClass.getName()+"的属性"+beanField.getName()+"成功");
								}
							} else
								logger.error("注入"+beanClass.getName()+"的属性"+beanField.getName()+"失败");
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
        logger.debug("注入Bean属性结束");
	}
 
    @SuppressWarnings("unchecked")
	public static <T> T getBean(Class<T> cls) {
        return (T) beanMap.get(cls);
    }
    
    protected static ActionMapping getActionMapping(String target) {
    	if (Util.isEmpty(target)) {
			return null;
		}
    	for (String key : actionMappingMap.keySet()) {
    		ActionMapping actionMapping = actionMappingMap.get(key);
			if (target.equals(actionMapping.getPath())) {
				return actionMapping;
			}
		}
    	return null;
    }

    protected static void stop() {
		actionMappingMap.clear();
		beanMap.clear();
	}

}